#include <algorithm> // std::swap
#include <functional> // std::function
namespace mytmpl {
struct __RefCnts {
    int ref_cnt;
    int weak_ref_cnt;
    __RefCnts(int ref, int weak_ref) : ref_cnt(ref), weak_ref_cnt(weak_ref) {}
};
template <typename T>
using __Deletor = std::function<void(T*)>;
template <typename T>
struct __DefaultDeletor {
    void operator()(T *x) { delete x; }
};
template <typename T> 
class weak_ptr;
template <typename T>
struct shared_ptr {
    T *p;
    __RefCnts *rcp;
    __Deletor<T> deletor;
    shared_ptr(T*x=nullptr, __Deletor<T> del = __DefaultDeletor<T>()): 
               p(x), rcp(nullptr), deletor(del) {
        if(p) rcp = new __RefCnts(1, 0);
    }
    ~shared_ptr() {
        if(rcp && --rcp->ref_cnt == 0) {
            deletor(p);
            if(rcp->weak_ref_cnt==0) {
                delete rcp;
            }
        }
    }
    shared_ptr(const shared_ptr<T> &sp) :
               p(sp.p), rcp(sp.rcp), deletor(sp.deletor) {
        if(rcp) rcp->ref_cnt++;
    }
    shared_ptr(shared_ptr<T> &&sp) : p(sp.p), rcp(sp.rcp), deletor(sp.deletor) {
        sp.p = nullptr;
        sp.rcp = nullptr;
    }
    shared_ptr& operator=(const shared_ptr<T> &sp) {
        if(this == &sp) return *this;
        if(rcp && --rcp->ref_cnt == 0 ) {
            deletor(p);
            if(rcp->weak_ref_cnt==0) {
                delete rcp;
            }
        }
        p = sp.p;
        rcp = sp.rcp;
        if(rcp) rcp->ref_cnt++;
        return *this;
    }
    shared_ptr& operator=(shared_ptr<T> &&sp) {
        if(this == &sp) return *this;
        if(rcp && --rcp->ref_cnt == 0 ) {
            deletor(p);
            if(rcp->weak_ref_cnt==0) {
                delete rcp;
            }
        }
        p = sp.p;
        rcp = sp.rcp;
        sp.p = nullptr;
        sp.rcp = nullptr;
        return *this;
    }
    T& operator*(){
        return *p;
    }
    T* operator->() {
        return p;
    }
    int use_count() { return rcp ?rcp->ref_cnt: 0; }
    operator bool() { return p != nullptr; }
    T *get() { return p; }
    void reset(T *x = nullptr) {
        if(rcp && --rcp->ref_cnt==0) {
            deletor(p);
            if(rcp->weak_ref_cnt==0) {
                delete rcp;
            }
        }
        p = x;
        if(p) rcp = new __RefCnts(1, 0);
        else rcp = nullptr;
    }
    void swap(shared_ptr<T> &sp) {
        std::swap(p, sp.p);
        std::swap(rcp, sp.rcp);
    }
    bool unique() { return rcp && rcp->ref_cnt==1; }
    bool operator==(const shared_ptr<T> &sp) {
        return rcp == sp.rcp;
    }
    bool operator==(const  T *x) {
        return p == x;
    }
    bool operator!=(const shared_ptr<T> &sp) {
        return rcp != sp.rcp;
    }
    bool operator!=(const  T *x) {
        return p != x;
    }
    __Deletor<T> get_deletor() { return deletor; }
    void set_deletor(__Deletor<T> del) { deletor = del; }
private:
    shared_ptr(const weak_ptr<T> &wp) : 
               p(wp.p), rcp(wp.rcp), deletor(__DefaultDeletor<T>()) {
        if(rcp) {
            if(rcp->ref_cnt == 0) {
                p = nullptr;
                rcp = nullptr;
            }else {
                rcp->ref_cnt++;
            }
        }
    }
    friend weak_ptr<T>;
};
template <typename T>
struct weak_ptr {
    T *p;
    __RefCnts *rcp;
    weak_ptr() : p(nullptr), rcp(nullptr) { }
    weak_ptr(shared_ptr<T> &sp) : p(sp.p), rcp(sp.rcp) {
        if(rcp) {
            ++rcp->weak_ref_cnt;
        }
    }
    weak_ptr(const weak_ptr<T> &wp) : p(wp.p), rcp(wp.rcp) {
        if(rcp) {
            ++rcp->weak_ref_cnt;
        }
    }
    weak_ptr(weak_ptr<T> &&wp) : p(wp.p), rcp(wp.rcp) {
        wp.p = nullptr;
        wp.rcp = nullptr;
    }
    weak_ptr& operator=(const weak_ptr<T> &wp) {
        if(this == &wp) return *this;
        if(rcp && --rcp->weak_ref_cnt==0 && rcp->ref_cnt==0) {
            delete rcp;
        }
        p = wp.p;
        rcp = wp.rcp;
        if(rcp) {
            ++rcp->weak_ref_cnt;
        }
        return *this;
    }
    weak_ptr& operator=(weak_ptr<T> &&wp) {
        if(this == &wp) return *this;
        if(rcp && --rcp->weak_ref_cnt==0 && rcp->ref_cnt==0) {
            delete rcp;
        }
        p = wp.p;
        rcp = wp.rcp;
        wp.p = nullptr;
        wp.rcp = nullptr;
        return *this;
    }
    weak_ptr& operator=(shared_ptr<T> &sp) {
        return *this = weak_ptr<T>(sp);
    }
    ~weak_ptr() {
        if(rcp && --rcp->weak_ref_cnt==0 && rcp->ref_cnt==0) 
            delete rcp;
    }
    shared_ptr<T> lock() {
        return shared_ptr<T>(*this);   
    }
    void swap(weak_ptr<T> &wp) {
        swap(p, wp.p);
        swap(rcp, wp.rcp);
    }
    void reset() {
        if(rcp) {
            if(--rcp->weak_ref_cnt==0 && rcp->ref_cnt==0)
                delete rcp;   
            rcp = nullptr;
            p = nullptr;    
        }        
    } 
    bool expired(){
        return rcp==NULL || rcp->ref_cnt==0;
    }
    int use_count() { return rcp ?rcp->ref_cnt: 0; }
    
};
template <typename T, typename Functor = __DefaultDeletor<T>>
struct unique_ptr {
    __Deletor<T> deletor;
    using _self_type = unique_ptr<T, Functor>;
    T *p;
    unique_ptr(T *x=nullptr, Functor del = Functor()) : 
        p(x), deletor(del) {}
    T& operator*() { return *p; }
    T* operator->() { return p; }
    T& operator[](int idx) { return p[idx]; }
    unique_ptr(const _self_type& up) = delete;
    unique_ptr(_self_type && up) : p(up.p), deletor(up.deletor) {
        up.p = nullptr;
    }
    unique_ptr<T>& operator=(const _self_type& up) = delete;
    unique_ptr<T>& operator=(_self_type && up) {
        if(p) deletor(p);
        p = up.p;
        up.p = nullptr;
        return *this;
    }
    T* get() { return p; }
    operator bool() { return p!=nullptr; }
    T *release() {
        T *tp = p;
        p = nullptr;
        return tp;
    }
    void reset(T *x=nullptr) {
        deletor(p);
        p = x;
    }
    void swap(_self_type &up) {
        std::swap(up.p, p);
    }
    bool operator==(const _self_type &up) {
        return up.p == p;
    }
    bool operator==(T *x) { return x==p; }
    bool operator!=(const _self_type &up) {
        return up.p != p;
    }
    bool operator!=(T *x) { return x!=p; }
    __Deletor<T> get_deletor() { return deletor; }
    void set_deletor(__Deletor<T> del) { deletor = del; }
};


} //NS: mytmpl
